JBoss Community Archive (Read Only)

SwitchYard

Message Composition

Message Composers

A MessageComposer can compose or decompose a native binding message to/from SwitchYard's canonical message.  A MessageComposer does this in three steps:

  1. Construct a new target message instance.

  2. Copy the content ("body") of the message.

  3. Delegate the header/property mapping to a ContextMapper.

We currently provide a SOAPMessageComposer, a CamelMessageComposer, and a HornetQMessageComposer.  These default implementations are used by their associated bindings, but can be overridden by the user.

Custom Message Composers

To implement a custom MessageComposer, you need to implement the org.switchyard.component.common.composer.MessageComposer interface:

public interface MessageComposer<T> {
    ContextMapper<T> getContextMapper();
    MessageComposer<T> setContextMapper(ContextMapper<T> contextMapper);
    Message compose(T source, Exchange exchange, boolean create) throws Exception;
    public T decompose(Exchange exchange, T target) throws Exception;
}
  • The getContextMapper() and setContextMapper() methods are just bean properties. If you extend BaseMessageComposer, both of these are implemented for you.

  • Your compose() method needs to take the data from the passed in source native message and compose a SwitchYard Message based on the specified Exchange.  The create parameter is whether or not we ask the Exchange to create a new Message, or just get the existing one.

  • Your decompose() method needs to take the data from the SwitchYard Message in the specified Exchange and decompose it into the target native message.

Then, specify your implementation in your switchyard.xml:

<binding.xyz ...>
    <messageComposer class="com.example.MyMessageComposer"/>
</binding.xyz>

Context Mappers

A ContextMapper moves native binding message headers and/or properties to/from SwitchYard's canonical context.  We provide many ContextMapper implementations OOTB (see section below).  These default implementations are used by their associated bindings, but can be overridden by the user.

Custom Context Mappers

To implement a custom ContextMapper, you need to implement the org.switchyard.component.common.composer.ContextMapper interface:

public interface ContextMapper<T> {
    void mapFrom(T source, Context context) throws Exception;
    void mapTo(Context context, T target) throws Exception;
}
  • Your mapFrom() method needs to map a source native message's properties to the SwitchYard Message's Context.

  • Your mapTo() method needs to map a SwitchYard Message's Context properties into the target native message.

  • If you extend BaseContextMapper, these methods are stubbed-out with no-op implementations so you only have to implement what you want to.

Then, specify your implementation in your switchyard.xml:

<binding.xyz ...>
    <contextMapper class="com.example.MyContextMapper"/>
</binding.xyz>

Alternatively, you can implement the org.switchyard.component.common.composer.RegexContextMapper.  The purpose of this interface is to add regular expression support, where you can filter exactly which Context properties get mapped:

public interface RegexContextMapper<T> extends ContextMapper<T> {
    ContextMapper<T> setIncludes(String includes);
    ContextMapper<T> setExcludes(String excludes);
    ContextMapper<T> setIncludeNamespaces(String includeNamespaces);
    ContextMapper<T> setExcludeNamespaces(String excludeNamespaces);
    boolean matches(String name);
    boolean matches(QName qname);
}
  • The setIncludes(), setExcludes(), setIncludeNamespaces() and setExcludeNamespaces() methods are just bean properties. The matches() methods use those bean properties to determine if the specified name or qualified name passes the collective regular expressions.

  • If you extend BaseRegexContextMapper, all of these are implemented for you.  Then, in your implementation's mapFrom / mapTo methods, you only need to first check if the property matches before you map it.

If your implementation extends RegexContextMapper, the following additional (regular expression valued) attributes of the <contextMapper/> element become meaningful/respected:

  • includes = Which context property names to include.

  • excludes = Which context property names to exclude.

  • includeNamespaces = Which context property namespaces to include (if the property name is a qualified name).

  • excludeNamespaces = Which context property namespaces to exclude (if the property name is a qualified name).

OOTB Implementation Notes

Note: All of the out-of-the-box implementations below extend BaseRegexContextMapper, thus all can be configured with the regular expression attributes described above.

  • The SOAPContextMapper, when processing an incoming SOAPMessage, takes the mime (in most cases, HTTP) headers from a soap envelope and maps them into the SwitchYard Context as Scope.MESSAGE properties with the SOAPComposition.SOAP_MESSAGE_MIME_HEADER label, and takes the soap header elements from the soap envelope and maps them into the SwitchYard Context as Scope.EXCHANGE properties with the SOAPComposition.SOAP_MESSAGE_HEADER label. When processing an outgoing SOAPMessage, it takes the SwitchYard Scope.MESSAGE Context properties and maps them into mime (in most cases, HTTP) headers, and takes the SwitchYard Scope.EXCHANGE Context properties and maps them into the soap envelope as soap header elements.

The SOAPContextMapper has an additional attribute that the other OOTB ContextMappers do not have: soapHeadersType:

<binding.soap>
    <contextMapper includes=".*" soapHeadersType="VALUE"/>
</binding.soap>

The value of soapHeadersType can be CONFIG, DOM, VALUE or XML (and correspond to the enum SOAPHeadersType.CONFIG, DOM, VALUE or XML).  With CONFIG, each soap header element is mapped into an org.switchyard.config.Configuration object, with DOM, each soap header element is left as is (a DOM element), with VALUE, just the String value of each soap header element is stored, and with XML, each soap header element is transformed into an XML String.

  • The CamelContextMapper, when processing an incoming CamelMessage, takes the CamelMessage headers and maps them into the SwitchYard Context as Scope.MESSAGE properties with the CamelComposition.CAMEL_MESSAGE_HEADER label, and takes the Camel Exchange properties and maps them into the SwitchYardContext as Scope.EXCHANGE properties with the CamelComposition.CAMEL_EXCHANGE_PROPERTY label.  When processing an outgoing CamelMessage, it takes the SwitchYard Scope.MESSAGE Context properties and maps them into the CamelMessage as headers, and takes the SwitchYard Scope.EXCHANGE Context properties and maps them into the Camel Exchange as properties.

  • The HornetQContextMapper, when processing an incoming ClientMessage, takes the ClientMessage properties and maps them into the SwitchYardContext as Scope.EXCHANGE properties with the HornetQCompsition.HORNETQ_MESSAGE_PROPERTY label.  When procesing an outgoing ClientMessage, it takes the SwitchYard Scope.EXCHANGE Context properties and maps them into the ClientMessage as properties.  There is no concept of "headers" in a HornetQ ClientMessage.

  • The HTTPContextMapper, when processing an incoming HTTP request, takes the incoming request headers and maps them into the SwitchYard Context as Scope.MESSAGE with the HttpComposition.HTTTP_HEADER label.  When processing an outgoing HTTP response, it takes the SwitchYard Scope.MESSAGE Context properties and maps them into the response headers.

  • The RESTEasyContextMapper, when processing an incoming HTTP request, takes the incoming request headers and maps them into the SwitchYard Context as Scope.MESSAGE with the RESTEasyComposition.HTTP_HEADER label.  When processing an outgoing HTTP response, it takes the SwitchYard Scope.MESSAGE Context properties and maps them into the response headers.

  • The JCA Component actually has 3 different ContextMappers:

    • The CCIIndexedRecordContextMapper, when processing an incoming IndexedRecord, takes the record name and record short description and maps them into the SwitchYardContext as Scope.EXCHANGE properties with the JCAComposition.JCA_MESSAGE_PROPERTY label.  When processing an outgoing IndexedRecord, it looks for those properties specifically in the SwitchYard.EXCHANGE Context properties by key and sets them on the IndexedRecord.

    • The CCIMappedRecordContextMapper, when processing an incoming MappedRecord, takes the record name and record short description and maps them into the SwitchYardContext as Scope.EXCHANGE properties with the JCAComposition.JCA_MESSAGE_PROPERTY label.  When processing an outgoing MappedRecord, it looks for those properties specifically in the SwitchYard.EXCHANGE Context properties by key and sets them on the MappedRecord.

    • The JMSContextMapper, when processing an incoming (JMS) Message, takes the Message properties and maps them into the SwitchYardContext as Scope.EXCHANGE properties with the JCAComposition.JCA_MESSAGE_PROPERTY label. When processing an outgoing (JMS) Message, it takes the SwitchYard.EXCHANGE Context properties and maps them into the Message as Object properties.

The reasoning of scoping headers as MESSAGE scope was modeled after the notion of http headers, where you will see some headers specifically useful for http requests, and other headers specifically useful for http responses.  In both cases, they are most likely tied to the binding's notion of an incoming message or an outgoing message.

The reasoning of scoping properties as EXCHANGE scope came from the idea that this is most likely application or domain data, and possibly useful in the entire processing of the Exchange.  An example of this would be a processInstanceId when using the BPM Component.

JBoss.org Content Archive (Read Only), exported from JBoss Community Documentation Editor at 2020-03-13 09:45:44 UTC, last content change 2014-11-16 17:47:33 UTC.